#include "operations.h"

//enum opcode { BEGIN, END, MOVE, ANL, ORL, XRL, SWAP, SETB, SJMP, JZ, ADDC, MULT, DIV, DISP, INPT, SUB, ADD };
enum opcode { ADD,SUB,MULT,DIV,MOV,COPY,JMP,JE,JNE,SJMP,SWAP,DISP };
enum valueType { INTEGER, FLOAT, REGISTER, OPCODE };

/* A register is a dynamic value (either int or double) that also lets you know what datatype it is. */
typedef struct {
		enum valueType type;
		union {
			int i;
			double f;
		} v;
} vm_Register;

/* The VM state consists of 10 registers and a pointer */
typedef struct {
		vm_Register reg[10];
		vm_Register tempreg;
		int pointer;
} vm_State;


/* A generic VM instruction.  A program is an array or linked-list of these. */
typedef struct {
		int a;
		int b;
		int c;

		double f;
		int is_floatop;

		void (*operation)(vm_State *vm, int a, int b, int c);
		void (*floatop)(vm_State *vm, int a, double b);
} vm_Instruction;

// a do-nothing vm function
void vm_NUL(vm_State *vm, int a, int b, int c) { }
void vm_NUL_float(vm_State *vm, int a, double b) { }

void vm_ADD(vm_State *vm, int a, int b, int c) {
		//printf("R%d + R%d\n",a,b);

		if ((vm->reg[a].type == INTEGER)&&(vm->reg[b].type == INTEGER))
			vm->reg[a].v.i += vm->reg[b].v.i;
		else if ((vm->reg[a].type == INTEGER)&&(vm->reg[b].type == FLOAT))
			vm->reg[a].v.i += vm->reg[b].v.f;
		else if ((vm->reg[a].type == FLOAT)&&(vm->reg[b].type == INTEGER))
			vm->reg[a].v.f += vm->reg[b].v.i;
		else if ((vm->reg[a].type == FLOAT)&&(vm->reg[b].type == FLOAT))
			vm->reg[a].v.f += vm->reg[b].v.f;
}
void vm_SUB(vm_State *vm, int a, int b, int c) {
		//printf("R%d - R%d\n",a,b);
		if ((vm->reg[a].type == INTEGER)&&(vm->reg[b].type == INTEGER))
			vm->reg[a].v.i -= vm->reg[b].v.i;
		else if ((vm->reg[a].type == INTEGER)&&(vm->reg[b].type == FLOAT))
			vm->reg[a].v.i -= vm->reg[b].v.f;
		else if ((vm->reg[a].type == FLOAT)&&(vm->reg[b].type == INTEGER))
			vm->reg[a].v.f -= vm->reg[b].v.i;
		else if ((vm->reg[a].type == FLOAT)&&(vm->reg[b].type == FLOAT))
			vm->reg[a].v.f -= vm->reg[b].v.f;
}
void vm_MULT(vm_State *vm, int a, int b, int c) {
		//printf("R%d * R%d\n",a,b);

		if ((vm->reg[a].type == INTEGER)&&(vm->reg[b].type == INTEGER))
			vm->reg[a].v.i *= vm->reg[b].v.i;
		else if ((vm->reg[a].type == INTEGER)&&(vm->reg[b].type == FLOAT))
			vm->reg[a].v.i *= vm->reg[b].v.f;
		else if ((vm->reg[a].type == FLOAT)&&(vm->reg[b].type == INTEGER))
			vm->reg[a].v.f *= vm->reg[b].v.i;
		else if ((vm->reg[a].type == FLOAT)&&(vm->reg[b].type == FLOAT))
			vm->reg[a].v.f *= vm->reg[b].v.f;
}
void vm_DIV(vm_State *vm, int a, int b, int c) {
		//printf("R%d / R%d\n",a,b);

		if ((vm->reg[a].type == INTEGER)&&(vm->reg[b].type == INTEGER))
			vm->reg[a].v.i /= vm->reg[b].v.i;
		else if ((vm->reg[a].type == INTEGER)&&(vm->reg[b].type == FLOAT))
			vm->reg[a].v.i /= vm->reg[b].v.f;
		else if ((vm->reg[a].type == FLOAT)&&(vm->reg[b].type == INTEGER))
			vm->reg[a].v.f /= vm->reg[b].v.i;
		else if ((vm->reg[a].type == FLOAT)&&(vm->reg[b].type == FLOAT))
			vm->reg[a].v.f /= vm->reg[b].v.f;
}

// sets register A equal to input value b
void vm_MOV(vm_State *vm, int a, int b, int c) {
	vm->reg[a].type = INTEGER;
	vm->reg[a].v.i = b;
}
// sets register A equal to input value b
void vm_MOV_float(vm_State *vm, int a, double b) {
	vm->reg[a].type = FLOAT;
	vm->reg[a].v.f = b;
}
// sets register a equal to register b
void vm_COPY(vm_State *vm, int a, int b, int c) {
	vm->reg[a] = vm->reg[b];		
}

//Causes the VM pointer to jump to a different position
//Ignores input of B and C
void vm_JMP(vm_State *vm, int a, int b, int c) {
	vm->pointer = a-2;
}

//alias for JMP
void vm_SJMP(vm_State *vm, int a, int b, int c) {
	vm_JMP(vm,a,b,c);
}

//Jumps to pointer c if registers Ra and Rb are equal.
void vm_JE(vm_State *vm, int a, int b, int c) {
	int eq = 0;

	if ((vm->reg[a].type == INTEGER)&&(vm->reg[b].type == INTEGER))
		eq = (vm->reg[a].v.i == vm->reg[b].v.i);
	else if ((vm->reg[a].type == INTEGER)&&(vm->reg[b].type == FLOAT))
		eq = (vm->reg[a].v.i == vm->reg[b].v.f);
	else if ((vm->reg[a].type == FLOAT)&&(vm->reg[b].type == INTEGER))
		eq = (vm->reg[a].v.f == vm->reg[b].v.i);
	else if ((vm->reg[a].type == FLOAT)&&(vm->reg[b].type == FLOAT))
		eq = (vm->reg[a].v.f == vm->reg[b].v.f);

	if (eq == 1)
		vm->pointer = c-2;
}
//Jumps to pointer c if registers Ra and Rb are not equal.
void vm_JNE(vm_State *vm, int a, int b, int c) {
	int eq = 1;

	if ((vm->reg[a].type == INTEGER)&&(vm->reg[b].type == INTEGER))
		eq = (vm->reg[a].v.i == vm->reg[b].v.i);
	else if ((vm->reg[a].type == INTEGER)&&(vm->reg[b].type == FLOAT))
		eq = (vm->reg[a].v.i == vm->reg[b].v.f);
	else if ((vm->reg[a].type == FLOAT)&&(vm->reg[b].type == INTEGER))
		eq = (vm->reg[a].v.f == vm->reg[b].v.i);
	else if ((vm->reg[a].type == FLOAT)&&(vm->reg[b].type == FLOAT))
		eq = (vm->reg[a].v.f == vm->reg[b].v.f);

	if (eq == 0)
		vm->pointer = c-2;
}

//Swaps contents of registers A and B
//Ignores input of C
void vm_SWAP(vm_State *vm, int a, int b, int c) {
	printf("R%d <-> R%d\n",a,b);
	vm_Register tempRegister;

	tempRegister = vm->reg[a];
	
	vm->reg[a] = vm->reg[b];
	vm->reg[b] = tempRegister;
}

//displays contents of Register[a]
//Ignores inputs B and C
void vm_DISP(vm_State *vm, int a, int b, int c) {
	
	if (vm->reg[a].type == INTEGER)
		printf("R%d: %d\n",a,vm->reg[a].v.i);
	else if (vm->reg[a].type == FLOAT)
		printf("R%d: %Ff\n",a,vm->reg[a].v.f);
	else
		printf("R%d: NULL\n",a);
}

